﻿/******************************************************************************
 * 
 * Announce: CSharpKit, Basic algorithms, components and definitions.
 *           Copyright (C) ShenYongchen.
 *           All rights reserved.
 *   Author: 申永辰.郑州 (shenyczz@163.com)
 *  WebSite: http://github.com/shenyczz/CSharpKit
 *
 * THIS CODE IS LICENSED UNDER THE MIT LICENSE (MIT).
 * THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF 
 * ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
 * IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
 * PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
 * 
******************************************************************************/

using System;

namespace CSharpKit.Numerics.LinearAlgebra
{
    // Matrix.Arithmetic（算法）
    partial class Matrix<T>
    {
        public Matrix<T> Negate()
        {
            var result = Builder.SameAs(this);
            DoNegate(result);
            return result;
        }


        public Matrix<T> Add(T scalar)
        {
            if (scalar.Equals(Zero))
            {
                return Clone();
            }

            var result = Builder.SameAs(this);
            DoAdd(scalar, result);
            return result;
        }
        public Matrix<T> Add(Matrix<T> other)
        {
            if (other.RowCount != RowCount || other.ColumnCount != ColumnCount)
            {
                throw new Exception("相加的两个矩阵行列不相等。。。");
            }

            var result = Builder.SameAs(this, other, RowCount, ColumnCount);
            DoAdd(other, result);
            return result;
        }


        public Matrix<T> Subtract(T scalar)
        {
            if (scalar.Equals(Zero))
            {
                return Clone();
            }

            var result = Builder.SameAs(this);
            DoSubtract(scalar, result);
            return result;
        }
        public Matrix<T> SubtractFrom(T scalar)
        {
            var result = Builder.SameAs(this);
            DoSubtractFrom(scalar, result);
            return result;
        }
        public Matrix<T> Subtract(Matrix<T> other)
        {
            if (other.RowCount != RowCount || other.ColumnCount != ColumnCount)
            {
                throw new Exception("相减的两个矩阵行列不相等。。。");
            }

            var result = Builder.SameAs(this, other, RowCount, ColumnCount);
            DoSubtract(other, result);
            return result;
        }


        public Matrix<T> Multiply(T scalar)
        {
            if (scalar.Equals(One))
            {
                return Clone();
            }

            if (scalar.Equals(Zero))
            {
                return Builder.SameAs(this);
            }

            var result = Builder.SameAs(this);
            DoMultiply(scalar, result);
            return result;
        }
        public Matrix<T> Multiply(Matrix<T> other)
        {
            // M * N => 要求 M.col == N.row => 结果 R[M.row, N.col]
            if (ColumnCount != other.RowCount)
            {
                throw DimensionsDontMatch<ArgumentException>(this, other);
            }

            var result = Builder.SameAs(this, other, RowCount, other.ColumnCount);
            DoMultiply(other, result);
            return result;
        }


        public Matrix<T> Divide(T scalar)
        {
            if (scalar.Equals(One))
            {
                return Clone();
            }

            if (scalar.Equals(Zero))
            {
                throw new DivideByZeroException();
            }

            var result = Builder.SameAs(this);
            DoDivide(scalar, result);
            return result;
        }
        public Matrix<T> DivideByThis(T scalar)
        {
            var result = Builder.SameAs(this);
            DoDivideByThis(scalar, result);
            return result;
        }

        public Matrix<T> Remainder(T divisor)
        {
            var result = Builder.SameAs(this);
            DoRemainder(divisor, result);
            return result;
        }
        public Matrix<T> RemainderByThis(T dividend)
        {
            var result = Builder.SameAs(this);
            DoRemainderByThis(dividend, result);
            return result;
        }
        public Matrix<T> PointwiseRemainder(Matrix<T> divisor)
        {
            if (ColumnCount != divisor.ColumnCount || RowCount != divisor.RowCount)
            {
                throw DimensionsDontMatch<ArgumentException>(this, divisor);
            }

            var result = Builder.SameAs(this, divisor);
            DoPointwiseRemainder(divisor, result);
            return result;
        }



        // 驻点运算

        public Matrix<T> PointwiseAbs()
        {
            var result = Builder.SameAs(this);
            DoPointwiseAbs(result);
            return result;
        }



        /// <summary>
        /// Multiplies the transpose of this matrix with another matrix and returns the result.
        /// </summary>
        /// <param name="other">The matrix to multiply with.</param>
        /// <exception cref="ArgumentException">If <strong>this.Rows != other.RowCount</strong>.</exception>
        /// <returns>The result of the multiplication.</returns>
        public Matrix<T> TransposeThisAndMultiply(Matrix<T> other)
        {
            if (RowCount != other.RowCount)
            {
                throw DimensionsDontMatch<ArgumentException>(this, other);
            }

            var result = Builder.SameAs(this, other, ColumnCount, other.ColumnCount);
            DoTransposeThisAndMultiply(other, result);
            return result;
        }



        public Vector<T> TransposeThisAndMultiply(Vector<T> rightSide)
        {
            //    if (RowCount != rightSide.Count)
            //    {
            //        throw DimensionsDontMatch<ArgumentException>(this, rightSide, "rightSide");
            //    }

            //    var result = Vector<T>.Builder.SameAs(this, rightSide, ColumnCount);
            //    DoTransposeThisAndMultiply(rightSide, result);
            //    return result;
            throw new NotImplementedException();
        }



        #region Do Functions

        protected virtual void DoNegate(Matrix<T> result)
        {
            Map(x => NumOperators.Negate(x), result, Zeros.AllowSkip);
        }

        protected virtual void DoAdd(T scalar, Matrix<T> result)
        {
            Map(x => NumOperators.Add(x, scalar), result, Zeros.AllowSkip);
        }

        protected virtual void DoAdd(Matrix<T> other, Matrix<T> result)
        {
            Map2((x, y) => NumOperators.Add(x, y), other, result, Zeros.AllowSkip);
        }


        protected virtual void DoSubtract(T scalar, Matrix<T> result)
        {
            Map(x => NumOperators.Subtract(x, scalar), result, Zeros.AllowSkip);
        }
        protected virtual void DoSubtractFrom(T scalar, Matrix<T> result)
        {
            Map(x => NumOperators.Subtract(scalar, x), result, Zeros.AllowSkip);
        }
        protected virtual void DoSubtract(Matrix<T> other, Matrix<T> result)
        {
            Map2((x, y) => NumOperators.Subtract(x, y), other, result, Zeros.AllowSkip);
        }


        protected virtual void DoMultiply(T scalar, Matrix<T> result)
        {
            Map(x => NumOperators.Multiply(x, scalar), result, Zeros.AllowSkip);
        }
        protected virtual void DoMultiply(Matrix<T> other, Matrix<T> result)
        {
            // 左行，右列
            int lrow = this.RowCount;
            int lcol = this.ColumnCount;

            int rrow = other.RowCount;
            int rcol = other.ColumnCount;

            // 左矩阵的列 == 右矩阵的行
            if (lcol != rrow)
            {
                throw new Exception("不符合矩阵乘法规则...");
            }

            for (var i = 0; i < lrow; i++)
            {
                for (var j = 0; j < rcol; j++)
                {
                    var s = Zero;

                    for (var k = 0; k < lcol; k++)
                    {
                        s = NumOperators.Add(s, NumOperators.Multiply(this[i, k], other[k, j]));
                    }

                    result[i, j] = s;
                }
            }

        }

        protected virtual void DoDivide(T scalar, Matrix<T> result)
        {
            Map(x => NumOperators.Divide(x, scalar), result, Zeros.AllowSkip);
        }
        protected virtual void DoDivideByThis(T scalar, Matrix<T> result)
        {
            Map(x => NumOperators.Divide(scalar, x), result, Zeros.AllowSkip);
        }


        protected virtual void DoRemainder(T divisor, Matrix<T> result)
        {
            Map(x => NumOperators.Modulo(x, divisor), result, Zeros.AllowSkip);
        }
        protected virtual void DoRemainderByThis(T dividend, Matrix<T> result)
        {
            Map(x => NumOperators.Modulo(dividend, x), result, Zeros.AllowSkip);
        }
        protected virtual void DoPointwiseRemainder(Matrix<T> divisor, Matrix<T> result)
        {
            Map2((x, y) => NumOperators.Modulo(x, y), divisor, result, Zeros.Include);
        }


        protected virtual void DoPointwiseAbs(Matrix<T> result)
        {
            Map(x => NumOperators.Abs(x), result, Zeros.AllowSkip);
        }


        protected virtual void DoTransposeThisAndMultiply(Matrix<T> other, Matrix<T> result)
        {
            throw new Exception();
        }


        // 一元逐点操作
        protected Matrix<T> PointwiseUnary(Action<Matrix<T>> action)
        {
            var result = Builder.SameAs(this);
            action(result);
            return result;
        }

        #endregion



        /// <summary>
        /// Complex conjugates each element of this matrix and place the results into the result matrix.
        /// </summary>
        /// <param name="result">The result of the conjugation.</param>
        protected virtual void DoConjugate(Matrix<T> result)
        {
            if (!ReferenceEquals(this, result))
            {
                CopyTo(result);
            }
        }




        /// <summary>
        /// 计算逆矩阵
        /// </summary>
        /// <returns></returns>
        public virtual Matrix<T> Inverse()
        {
            if (RowCount != ColumnCount)
            {
                throw new ArgumentException(Resources.Numerics.ArgumentMatrixSquare);
            }

            return LU().Inverse();
        }









        #region Exceptions - possibly move elsewhere?

        internal static Exception DimensionsDontMatch<TException>(Matrix<T> left, Matrix<T> right, Matrix<T> result, string paramName = null)
            where TException : Exception
        {
            var message = string.Format(Resources.Numerics.ArgumentMatrixDimensions3, left.RowCount + "x" + left.ColumnCount, right.RowCount + "x" + right.ColumnCount, result.RowCount + "x" + result.ColumnCount);
            return CreateException<TException>(message, paramName);
        }
        internal static Exception DimensionsDontMatch<TException>(Matrix<T> left, Matrix<T> right, string paramName = null)
            where TException : Exception
        {
            var message = string.Format(Resources.Numerics.ArgumentMatrixDimensions2, left.RowCount + "x" + left.ColumnCount, right.RowCount + "x" + right.ColumnCount);
            return CreateException<TException>(message, paramName);
        }
        internal static Exception DimensionsDontMatch<TException>(Matrix<T> matrix)
            where TException : Exception
        {
            var message = string.Format(Resources.Numerics.ArgumentMatrixDimensions1, matrix.RowCount + "x" + matrix.ColumnCount);
            return CreateException<TException>(message);
        }
        internal static Exception DimensionsDontMatch<TException>(Matrix<T> left, Vector<T> right, string paramName = null)
           where TException : Exception
        {
            return DimensionsDontMatch<TException>(left, right.ToColumnMatrix(), paramName);
        }

        /*
         internal static Exception DimensionsDontMatch<TException>(Matrix<T> left, Vector<T> right, Vector<T> result, string paramName = null)
             where TException : Exception
         {
             return DimensionsDontMatch<TException>(left, right.ToColumnMatrix(), result.ToColumnMatrix(), paramName);
         }

         internal static Exception DimensionsDontMatch<TException>(Vector<T> left, Matrix<T> right, string paramName = null)
             where TException : Exception
         {
             return DimensionsDontMatch<TException>(left.ToColumnMatrix(), right, paramName);
         }
         internal static Exception DimensionsDontMatch<TException>(Vector<T> left, Vector<T> right, string paramName = null)
             where TException : Exception
         {
             return DimensionsDontMatch<TException>(left.ToColumnMatrix(), right.ToColumnMatrix(), paramName);
         }
         */
         
        internal static Exception DimensionsDontMatch<TException>(Vector<T> left, Matrix<T> right, string paramName = null)
            where TException : Exception
        {
            return DimensionsDontMatch<TException>(left.ToColumnMatrix(), right, paramName);
        }


        static Exception CreateException<TException>(string message, string paramName = null)
            where TException : Exception
        {
            if (typeof(TException) == typeof(ArgumentException))
            {
                return new ArgumentException(message, paramName);
            }
            if (typeof(TException) == typeof(ArgumentOutOfRangeException))
            {
                return new ArgumentOutOfRangeException(paramName, message);
            }
            return new Exception(message);
        }

        #endregion

        //}}@@@
    }


}

